/*
## @file
#
#  Copyright (c) 2018 Loongson Technology Corporation Limited (www.loongson.cn).
#  All intellectual property rights(Copyright, Patent and Trademark) reserved.
#
#  Any violations of copyright or other intellectual property rights of the Loongson Technology
#  Corporation Limited will be held accountable in accordance with the law,
#  if you (or any of your subsidiaries, corporate affiliates or agents) initiate
#  directly or indirectly any Intellectual Property Assertion or Intellectual Property Litigation:
#  (i) against Loongson Technology Corporation Limited or any of its subsidiaries or corporate affiliates,
#  (ii) against any party if such Intellectual Property Assertion or Intellectual Property Litigation arises
#  in whole or in part from any software, technology, product or service of Loongson Technology Corporation
#  Limited or any of its subsidiaries or corporate affiliates, or (iii) against any party relating to the Software.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR
#  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION).
#
#
##
*/


#ifndef _LS7AINITLIB_H_
#define _LS7AINITLIB_H_

#include <linux/types.h>

#ifdef CONFIG_32BIT
extern u64 __raw__readq(u64 addr);
extern u64 __raw__writeq(u64 addr, u64 val);
extern u32 __raw__readw(u64 addr);
extern u32 __raw__writew(u64 addr, u32 val);
extern u16 __raw__readh(u64 addr);
extern u16 __raw__writeh(u64 addr, u16 val);
extern u8 __raw__readb(u64 addr);
extern u8 __raw__writeb(u64 addr, u8 val);

#define Readq(addr)		__raw__readq(addr)
#define Writeq(addr, val)	__raw__writeq(addr, val)
#define Readl(addr)		__raw__readw(addr)
#define Writel(addr, val)	__raw__writew(addr, val)
#define Read16(addr)		__raw__readh(addr)
#define Write16(addr, val)	__raw__writeh(addr, val)
#define Readb(addr)		__raw__readb(addr)
#define Writeb(addr, val)	__raw__writeb(addr, val)
#else
#define Readq(addr)		__raw_readq((void *)(addr))
#define Writeq(addr, val)	__raw_writeq(val, (void *)(addr))
#define Readl(addr)		__raw_readl((void *)(addr))
#define Writel(addr, val)	__raw_writel(val, (void *)(addr))
#define Read16(addr)		__raw_readw((void *)(addr))
#define Write16(addr, val)	__raw_writew(val, (void *)(addr))
#define Readb(addr)		__raw_readb((void *)(addr))
#define Writeb(addr, val)	__raw_writeb(val, (void *)(addr))
#endif


#define LS7A_HT_RX_WIN0_OFFSET          0x140
#define LS7A_HT_RX_WIN1_OFFSET          0x148
#define LS7A_HT_TX_POST_WIN0_OFFSET     0x170
#define LS7A_HT_TX_POST_WIN1_OFFSET     0x178

#if defined(LS3A4000)
#define ALLOW_EXT_IOI
#define LS3A_HT_RX_CACHE_WIN0_OFFSET    0x140
#define LS3A_HT_RX_CACHE_WIN1_OFFSET    0x148
#define LS3A_HT_RX_CACHE_WIN2_OFFSET    0x150
#define LS3A_HT_RX_CACHE_WIN3_OFFSET    0x158
#define LS3A_HT_RX_CACHE_WIN4_OFFSET    0x160
#define LS3A_HT_TX_POST_WIN0_OFFSET     0x170
#define LS3A_HT_TX_POST_WIN1_OFFSET     0x178
#define LS3A_HT_RX_UNCACHE_WIN0_OFFSET  0x190
#define LS3A_HT_RX_UNCACHE_WIN1_OFFSET  0x198
#define LS3A_HT_RX_UNCACHE_WIN2_OFFSET  0x1A0
#define LOONGSON_HT1_INT_TRANS_ADDR 0x90000efdfb000270ULL

#define LS3A_HT_FREQ                    0x4C
#define LS3A_HT_RETRY_CONTROL           0x64
#define LS3A_HT_REVISION                0x6C
#define LS3A_HT_LINK_TRAIN              0xD0
#define LS3A_HT_RX_BUS                  0x1C8
#define LS3A_HT_RX_BUFFER_DEFAULT       0x1DC
#define LS3A_HT_PLL_CONF                0x1F4
#elif defined(LS3A3000)
#define LS3A_HT_RX_CACHE_WIN0_OFFSET    0x60
#define LS3A_HT_RX_CACHE_WIN1_OFFSET    0x68
#define LS3A_HT_RX_CACHE_WIN2_OFFSET    0x70
#define LS3A_HT_RX_CACHE_WIN3_OFFSET    0x148
#define LS3A_HT_RX_CACHE_WIN4_OFFSET    0x150
#define LS3A_HT_TX_POST_WIN0_OFFSET     0xD0
#define LS3A_HT_TX_POST_WIN1_OFFSET     0xD8
#define LS3A_HT_RX_UNCACHE_WIN0_OFFSET  0xF0
#define LS3A_HT_RX_UNCACHE_WIN1_OFFSET  0xF8
#define LS3A_HT_RX_UNCACHE_WIN2_OFFSET  0x168

#define LS3A_HT_FREQ                    0x48
#define LS3A_HT_RETRY_CONTROL           0x118
#define LS3A_HT_REVISION                0x110
#define LS3A_HT_LINK_TRAIN              0x130
#define LS3A_HT_RX_BUS                  0x54
#define LS3A_HT_RX_BUFFER_DEFAULT       0x5C
#define LS3A_HT_PLL_CONF                0x178
#endif

#define LS7A_PLL_DIV_REFC_OFFSET 0
#define LS7A_PLL_LOCK_OFFSET     7
#define LS7A_PLL_SEL0_OFFSET     8
#define LS7A_PLL_SEL1_OFFSET     9
#define LS7A_PLL_SEL2_OFFSET     10
#define LS7A_PLL_SET_OFFSET      11
#define LS7A_PLL_BYPASS_OFFSET   12
#define LS7A_PLL_PD_OFFSET       13

#define LS7A_PLL_DIV0_OFFSET     0
#define LS7A_PLL_DIV1_OFFSET     7
#define LS7A_PLL_DIV2_OFFSET     14
#define LS7A_PLL_LOOPC_OFFSET    21

#define LS7A_PLL_VALUE(LOOPC, DIV2, DIV1, DIV0) ((LOOPC << LS7A_PLL_LOOPC_OFFSET) | (DIV2 << LS7A_PLL_DIV2_OFFSET) | (DIV1 << LS7A_PLL_DIV1_OFFSET) | (DIV0 << LS7A_PLL_DIV0_OFFSET))

//PCIE configuration space definitions
#define PCIE_TYPE0_VID           0x0
#define PCIE_TYPE0_DID           0x2
#define PCIE_TYPE0_CMD           0x4
#define PCIE_TYPE0_STS           0x6
#define PCIE_TYPE0_REV           0x8
#define PCIE_TYPE0_CLASS         0x9
#define PCIE_TYPE0_BAR0          0x10
#define PCIE_TYPE0_BAR1          0x14
#define PCIE_TYPE0_BAR2          0x18
#define PCIE_TYPE0_BAR3          0x1c
#define PCIE_TYPE0_BAR4          0x20
#define PCIE_TYPE0_BAR5          0x24
#define PCIE_TYPE0_SUB_SYS_VID   0x2c
#define PCIE_TYPE0_SUB_SYS_ID    0x2e

#define PCIE_TYPE0_CMD_MEM_SPACE_ENABLE (0x1 << 1)

//if board use PCIE Port0 but PRSNTn0 is not correctly connected, you need force enable it
//#define FORCE_ENABLE_PCIE_F0_P0
//#define FORCE_ENABLE_PCIE_F1_P0
//#define FORCE_ENABLE_PCIE_G0_P0
//#define FORCE_ENABLE_PCIE_G0_P0
//#define FORCE_ENABLE_PCIE_H_P0

#define LS7A_SATA0_DISABLE  0
#define LS7A_SATA1_DISABLE  0
#define LS7A_SATA2_DISABLE  0
#define LS7A_USB0_DISABLE   0
#define LS7A_USB1_DISABLE   0
#define LS7A_LPC_DISABLE    0
#define LS7A_GMEM_CFG       1
#define OVRD_SATA_PHY       0

//#define USE_PCIE_PAD_REFCLK
//#define USE_SATA_PAD_REFCLK
#define USE_USB_SYS_REFCLK

#define CONF_HT_CLKEN_OFFSET        0x418
#define CONF_HT_ROUTE_OFFSET        0x41c
#define CONF_NB_OFFSET              0x420
#define CONF_SB_OFFSET              0x430
#define CONF_PAD_OFFSET             0x438
#define CONF_PLL0_OFFSET            0x480
#define CONF_PLL1_OFFSET            0x490
#define CONF_PLL2_OFFSET            0x4a0
#define CONF_PLL3_OFFSET            0x4b0
#define CONF_PLL4_OFFSET            0x4c0
#define CONF_PCIE_F0_REG_OFFSET     0x588
#define CONF_PCIE_F0_PHY_OFFSET     0x590
#define CONF_PCIE_F1_REG_OFFSET     0x5a8
#define CONF_PCIE_F1_PHY_OFFSET     0x5b0
#define CONF_PCIE_H_REG_OFFSET      0x5c8
#define CONF_PCIE_H_PHY_LO_OFFSET   0x5d0
#define CONF_PCIE_H_PHY_HI_OFFSET   0x5d8
#define CONF_PCIE_G0_REG_OFFSET     0x5e8
#define CONF_PCIE_G0_PHY_LO_OFFSET  0x5f0
#define CONF_PCIE_G0_PHY_HI_OFFSET  0x5f8
#define CONF_PCIE_G1_REG_OFFSET     0x608
#define CONF_PCIE_G1_PHY_LO_OFFSET  0x610
#define CONF_PCIE_G1_PHY_HI_OFFSET  0x618

#define CONF_USB0_PHY_OFFSET  0x700
#define CONF_USB1_PHY_OFFSET  0x710

#define CONF_SATA0_REG_OFFSET 0x740
#define CONF_SATA0_PHY_OFFSET 0x748
#define CONF_SATA1_REG_OFFSET 0x750
#define CONF_SATA1_PHY_OFFSET 0x758
#define CONF_SATA2_REG_OFFSET 0x760
#define CONF_SATA2_PHY_OFFSET 0x768

#define CONF_GMEM_BAR_MASK_OFFSET 0x3838
#define CONF_CHIP_ID_OFFSET       0x3ff8

#define HT1_MEM_BASE_ADDR       0x90000e0000000000
#define CONFBUS_BASE_ADDR       0x10010000
#define LS7A_CONFBUS_BASE_ADDR  (HT1_MEM_BASE_ADDR | CONFBUS_BASE_ADDR)
#define TEMP_GMEM_ADDR          0x40000000
#define LS7A_GMEM_TEMP_ADDR (HT1_MEM_BASE_ADDR | TEMP_GMEM_ADDR)
#define MC_REGS_COUNT 118


#define HT_CONF_TYPE0_ADDR             0x90000efdfe000000
#define HT_CONF_TYPE1_ADDR             0x90000efdff000000
#define HT1_CONTROLLER_CONF_BASE_ADDR  0x90000efdfb000000
#define HEADER_ADDR(X,Y)               (HT_CONF_TYPE0_ADDR | (X << 11) | (Y << 8))
#define GPU_HEADER_ADDR                HEADER_ADDR(6, 0)

#define NODE_OFFSET 44

#define INT_BASE_ADDR                  0x10000000
#define HPET_BASE_ADDR                 0x10001000
#define CONFBUS_BASE_ADDR              0x10010000
#define MISC_BASE_ADDR                 0x10080000
#define LS7A_ACPI_OFFSET               0x50000
#define LS7A_RTC_REG_OFFSE             0x50100

#define LS7A_MISC_BASE                  (HT1_MEM_BASE_ADDR | MISC_BASE_ADDR)
#define LS7A_ACPI_BASE                  (LS7A_MISC_BASE | LS7A_ACPI_OFFSET)
#define LS7A_RTC_REG_BASE               (LS7A_MISC_BASE + LS7A_RTC_REG_OFFSE)

#define LS132ENABLE_BASE               0x900000001fe00000
#define DYNAMIC_FREQ_SCALE             0x0008
#define DVFS_RESET                     0x420

#define LS7A_PWM0_REG_BASE              (LS7A_MISC_BASE + 0x20000)
#define LS7A_PWM0_LOW                   (LS7A_PWM0_REG_BASE + 0x4)
#define LS7A_PWM0_FULL                  (LS7A_PWM0_REG_BASE + 0x8)
#define LS7A_PWM0_CTRL                  (LS7A_PWM0_REG_BASE + 0xc)

#define LS7A_PWM1_REG_BASE              (LS7A_MISC_BASE + 0x20100)
#define LS7A_PWM1_LOW                   (LS7A_PWM1_REG_BASE + 0x4)
#define LS7A_PWM1_FULL                  (LS7A_PWM1_REG_BASE + 0x8)
#define LS7A_PWM1_CTRL                  (LS7A_PWM1_REG_BASE + 0xc)

#define LS7A_PWM2_REG_BASE              (LS7A_MISC_BASE + 0x20200)
#define LS7A_PWM2_LOW                   (LS7A_PWM2_REG_BASE + 0x4)
#define LS7A_PWM2_FULL                  (LS7A_PWM2_REG_BASE + 0x8)
#define LS7A_PWM2_CTRL                  (LS7A_PWM2_REG_BASE + 0xc)

#define LS7A_PWM3_REG_BASE              (LS7A_MISC_BASE + 0x20300)
#define LS7A_PWM3_LOW                   (LS7A_PWM3_REG_BASE + 0x4)
#define LS7A_PWM3_FULL                  (LS7A_PWM3_REG_BASE + 0x8)
#define LS7A_PWM3_CTRL                  (LS7A_PWM3_REG_BASE + 0xc)

/* Ls7a ACPI Register */
#define ACPI_PMCON_RESUME_REG_OFFSET   0x4
#define ACPI_PM1_STS_REG_OFFSET        0xc
#define ACPI_PM1_CNT_REG_OFFSET        0x14
#define ACPI_GPE0_STS_REG_OFFSET       0x28
#define ACPI_GPE0_EN_REG_OFFSET        0x2c
#define ACPI_RST_CNT_REG_OFFSET        0x30
#define ACPI_RTC1_OFFSET               0x50
#define ACPI_RTC2_OFFSET               0x54
#define ACPI_RESET_VAL                 0x1
#define ACPI_PM1_STS_CLEAN_VAL         0xffffffff
#define ACPI_SHUT_DOWN_VAL             0x3c00

#define LS7A_VERSION	(!(Readb(0x90000efdfe000108) == 0))

#define LS7A_PCIE_CFG_READ(data,reg)                   \
	Readl(0x90000e0000000000 + data + reg)

#define LS7A_PCIE_CFG_WRITE(data,reg,val)               \
	Writel(0x90000e0000000000 + data + reg, val)

#define PCIE_STAT_CHECK_TIMES 200000

#define LS7A_PCIE_CFG(addr, data, flag, end)                     \
    Val32 = Readl(0x90000efe08000000 + addr + 0xc);              \
    Val32 &= 0xfff9ffff;                                         \
    Val32 |= (PCIE_TRY_GEN2 << 17) | (PCIE_TX_FULL_SWING << 18); \
    Writel(0x90000efe08000000 + addr + 0xc, Val32);              \
                                                                 \
    Val32 = Readl(0x90000efe07000000 + addr + 0x1c);             \
    Val32 |= (0x1 << 26);                                        \
    Writel(0x90000efe07000000 + addr + 0x1c, Val32);             \
                                                                 \
    Writel(0x90000efe00000000 + addr + 0x10, data);              \
                                                                 \
    Val32 = Readl(0x90000e0000000000 + data + 0x54);             \
    Val32 &= ~((0x7 << 18) | (0x7 << 2));                        \
    Writel(0x90000e0000000000 + data + 0x54, Val32);             \
                                                                 \
    Val32 = Readl(0x90000e0000000000 + data + 0x58);             \
    Val32 &= ~((0x7 << 18) | (0x7 << 2));                        \
    Writel(0x90000e0000000000 + data + 0x58, Val32);             \
                                                                 \
    Writel(0x90000e0000000000 + data + 0x24, 0);                 \
                                                                 \
    Writel(0x90000e0000000000 + data, 0xff204c);                 \
    /*read link state */                                         \
    __asm__ ("sync");                                            \
    WaitTime = PCIE_STAT_CHECK_TIMES;                            \
    do{                                                          \
      if((LS7A_PCIE_CFG_READ(data,0xc) & 0x3f) == 0x11){         \
        goto end;                                                \
      }                                                          \
      WaitTime--;                                                \
    }while(WaitTime);                                            \
    Data |= flag;                                                \
end:

#define LS7A_PCIE_CFG_DONE(addr)                     \
    Writel(0x90000efe00000000 + addr + 0x10, 0x0);

#define LS7A_PCIE_CLEAR_PORT_IRQ(addr)                    \
{                                                         \
    u64 new_addr = 0x90000efe00000000 + addr + 0x10;      \
    Writel(new_addr, 0x60000000);                         \
    do{                                                   \
      if (LS7A_PCIE_CFG_READ(0x60000000,0x18)) {          \
        u32 val = LS7A_PCIE_CFG_READ(0x60000000,0x18);    \
        LS7A_PCIE_CFG_WRITE(0x60000000,0x1c,val);         \
        break;                                            \
      }                                                   \
    } while(0);                                           \
    Writel(new_addr, 0x0);                                \
}

#define LS7A_PCIE_RST_CTRL(data)                                         \
    Writel(LS7A_CONFBUS_BASE_ADDR + CONF_NB_OFFSET, Readl(LS7A_CONFBUS_BASE_ADDR + CONF_NB_OFFSET) & ~(data << 1));      \
    Writel(LS7A_CONFBUS_BASE_ADDR + CONF_NB_OFFSET, Readl(LS7A_CONFBUS_BASE_ADDR + CONF_NB_OFFSET) | (data));            \
    udelay(10000);                                             \
    Writel(LS7A_CONFBUS_BASE_ADDR + CONF_NB_OFFSET, Readl(LS7A_CONFBUS_BASE_ADDR + CONF_NB_OFFSET) & ~(data));           \
    udelay(10000);                                             \
    switch (data) {                                                      \
    case (1<<20):                                                        \
      do{                                                                \
      if((Readl(LS7A_CONFBUS_BASE_ADDR + 0x424) >> 14) & 0x1)            \
        break;                                                           \
      }while(1);                                                         \
      break;                                                             \
    case (1<<24):                                                        \
      do{                                                                \
      if((Readl(LS7A_CONFBUS_BASE_ADDR + 0x424) >> 16) & 0x1)            \
        break;                                                           \
      }while(1);                                                         \
      break;                                                             \
    case (1<<28):                                                        \
      do{                                                                \
      if((Readl(LS7A_CONFBUS_BASE_ADDR + 0x424) >> 18) & 0x1)            \
        break;                                                           \
      }while(1);                                                         \
      break;                                                             \
    default:                                                             \
      break;                                                             \
    }                                                                    \
    Writel(LS7A_CONFBUS_BASE_ADDR + CONF_NB_OFFSET, Readl(LS7A_CONFBUS_BASE_ADDR + CONF_NB_OFFSET) | (data << 1));

#define LS7A_PCIE_LINK_VAL_RESTORE(addr)                   \
	Val32Bak =  Readl(0x90000efe00000000 + addr + 0x4);    \
	Writel(0x90000efe00000000 + addr + 0x18, 0x50500);     \
	Writel(0x90000efe00000000 + addr + 0x4, 0x147);

#define LS7A_PCIE_LINK_VAL_BACKUP(addr)                    \
      Writel(0x90000efe00000000 + addr + 0x18, 0);         \
      Writel(0x90000efe00000000 + addr + 0x4, Val32Bak);

#define PCIE_MAX_SIZE 12

#define SET_HT_REG_DISABLE_UEFI(node, value) \
	Writeq(0x900000003ff00400 | ((u64)node << 44), Readq(0x900000003ff00400 | ((u64)node << 44)) & ~((u64)0xf << 44));\
	Writeq(0x900000003ff00400 | ((u64)node << 44), Readq(0x900000003ff00400 | ((u64)node << 44)) | ((u64)value << 44))

#define ENABLE_XLINK_UEFI(node) \
	Writeq(0x900000003ff00400 | ((u64)node << 44), Readq(0x900000003ff00400 | ((u64)node << 44)) | ((u64)0x1 << 8))

void Ls7aGmacPhyInit(void);

typedef struct CurrentSc {
	unsigned int Clk1;
	unsigned int Clk0;
	unsigned int CurrentScore;
} CurrentScore;

typedef struct _smartfan {
	u32 MinRpm;
	u32 MaxRpm;
} SmartFan;

typedef struct _ls7apcieinfo {
	u32 PcieCfgBuffer[PCIE_MAX_SIZE];
	u32 Num;
} Ls7aPcieInfo;

#define PCIE_PD_LOOP    20
#endif
